#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "log.hpp"
#include "epoller.hpp"
const int fd_num_max = sizeof(fd_set) * 8; // 4096
int defaultfd = -1;
uint16_t defaultport = 8080;
uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);
class epoll_server
{
public:
    epoll_server(uint16_t port = defaultport)
        : _port(port), _listsocket_ptr(new Sock()), _epoller_ptr(new epoller())

    {
    }
    ~epoll_server()
    {
        _listsocket_ptr->Close();
    }
    void Init()
    {
        _listsocket_ptr->Socket();
        _listsocket_ptr->Bind(_port);
        _listsocket_ptr->Listen();

        lg(Info, "create listen socket success: %d\n", _listsocket_ptr->get_sockfd());
    }

    void Start()
    {
        // 这里只监听listensockfd
        int n = _epoller_ptr->Epoll_Update(EPOLL_CTL_ADD, _listsocket_ptr->get_sockfd(), EVENT_IN);
        struct epoll_event recv[num];
        while (1)
        {
            int n = _epoller_ptr->Epoll_Wait(recv, num);
            // 返回值n:返回对应I/O上已准备好的文件描述符数目，如返回0表示已超时, 返回小于0表示函数失败
            // recv保存等到的fd事件属性
            // 等到之后，分配任务
            if (n > 0)
            {
                // 有事件就绪
                lg(Debug, "event happened, fd is : %d", recv[0].data.fd);
                Dispatcher(recv, n);
            }
            else if (n == 0)
            {
                lg(Info, "time out ...");
            }
            else
            {
                lg(Error, "epll wait error");
            }
        }
    }

    // num为等到的个数
    void Dispatcher(struct epoll_event *recv, int num)
    {
        for (int i = 0; i < num; i++)
        {
            uint32_t events = recv[i].events;
            int fd = recv[i].data.fd;
            // 这里是等到listensockfd然后进行链接，在链接函数中继续调用Epoll_Update来等待用于通信的sockfd
            if (events == EVENT_IN)
            {
                if (fd == _listsocket_ptr->get_sockfd())
                {
                    Accepter();
                }
                // 用于通信的fd  接收内容
                else
                {
                    Reciver(fd);
                }
            }
        }
    }

    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sockfd = _listsocket_ptr->Accept(clientip, &clientport);
        if (sockfd > 0)
        {
            // 链接好后进行监听  等待发送内容
            int n = _epoller_ptr->Epoll_Update(EPOLL_CTL_ADD, sockfd, EVENT_IN);
        }
    }

    void Reciver(int fd)
    {
        // 需要将fd设置为非阻塞读取，在ET模式下，只有一次读取机会，
        // 因此必须一次性把数据读完(但是一次性的话难免会被其他的打断读取，所以采用非阻塞来读取更加保险)

        // 先直接用read读取
        char buffer[1024];
        int n = read(fd, buffer, sizeof(buffer) - 1); // 这里采用的是阻塞式的等待
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "get a messge: " << buffer << endl;
        }
        // 返回0，表示客户端关闭链接了 移除文件描述符
        else if (n == 0)
        {
            lg(Info, "client quit, me too, close fd is : %d", fd);
            _epoller_ptr->Epoll_Update(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        // 出错了
        else
        {
            lg(Info, "client error, me too, close fd is : %d", fd);
            _epoller_ptr->Epoll_Update(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }

private:
    std::shared_ptr<Sock> _listsocket_ptr;
    std::shared_ptr<epoller> _epoller_ptr;
    uint16_t _port;
    static const int num = 64;
};
